/**
 * \brief Pointer that has different behaviors depending on template type.
 *
 * \author Bartosz Kedra (bartosz.kedra@gmail.com)
 */

#include <exception>
#include <cstdio>
#include <iostream>
#include <string>
#include <assert.h>

struct NullPointerException : public std::exception {

   const char* what()
   {
      return "NullPointerException";
   }
};

struct NoCheck {};
struct CheckInit : public NoCheck {};
struct CheckDeref : public NoCheck {};
struct CheckAll : public CheckInit, public CheckDeref {};

template <typename T, typename Checking = CheckAll> class SmartPtr
{

public:
   SmartPtr(T *p): _p(p)
   {
      init(Checking());
   }

   T& get()
   {
      get(Checking());
      return *_p;
   }

private:
   void check() const { if(!_p) throw NullPointerException(); }

   void init(NoCheck) { }
   void init(CheckInit) { check(); }

   void get(NoCheck) { }
   void get(CheckDeref) { check(); }
   T *_p;
};


typedef SmartPtr<int, NoCheck> PNoCheck;
typedef SmartPtr<int, CheckInit> PCheckInit;
typedef SmartPtr<int, CheckDeref> PCheckDeref;
typedef SmartPtr<int, CheckAll> PCheckAll;

int main()
{
   PNoCheck(0L);
   PCheckDeref pCheckDeref(0L);
   try
   {
      PCheckInit(0L);
      assert(false);
   }
   catch (NullPointerException e)
   {
      std::cout << e.what() << std::endl;
   }

   try
   {
      PCheckAll(0L);
      assert(false);
   }
   catch (NullPointerException e)
   {
      std::cout << e.what() << std::endl;
   }

   try
   {
      pCheckDeref.get();
      assert(false);
   }
   catch (NullPointerException e)
   {
      std::cout << e.what() << std::endl;
   }
   return 0;
}


